feat: add downloadBaseURL, checksum, and downloadAuthToken inputs (closes Azure#206)#268
feat: add downloadBaseURL, checksum, and downloadAuthToken inputs (closes Azure#206)#268benjaminbob21 wants to merge 1 commit into
Conversation
5bc2efb to
d7e6712
Compare
d7e6712 to
4cce71c
Compare
There was a problem hiding this comment.
Pull request overview
Adds support for downloading kubectl from custom/private mirrors by introducing a configurable download base URL, optional SHA256 verification, and optional bearer-token authentication. This extends the action’s download pipeline with additional URL validation and a “secure download” path intended to mitigate SSRF/token leakage risks when using non-default mirrors.
Changes:
- Add new action inputs:
downloadBaseURL,checksum,downloadAuthToken, and route downloads through mirror-aware logic. - Implement URL/version validation and a redirect-aware
secureDownload()path for custom mirrors, plus optional SHA256 verification. - Expand unit and integration tests to cover custom mirrors, redirects/SSRF guards, and checksum validation.
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
src/run.ts |
Plumbs new inputs into version resolution and download/caching, adds checksum verification and token handling. |
src/run.test.ts |
Adds extensive test coverage for custom mirror behavior, URL/version validation, redirects, auth header stripping, and checksum checks. |
src/helpers.ts |
Introduces secureDownload, base URL/version validation helpers, and baseURL-aware URL construction for downloads/version discovery. |
package.json |
Adds @actions/http-client dependency used by secureDownload. |
package-lock.json |
Locks the newly added @actions/http-client dependency. |
action.yml |
Documents and exposes the new inputs to action consumers. |
.github/workflows/integration-tests.yml |
Adds an integration test validating checksum success and failure behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (status !== 200) { | ||
| response.message.resume() | ||
| throw new toolCache.HTTPError(status) | ||
| } |
| // Scope the cache key per mirror so a binary cached from one source isn't silently reused for another. | ||
| const cacheKey = isDefaultBaseURL(baseURL) | ||
| ? version | ||
| : `${version}-${crypto | ||
| .createHash('sha256') | ||
| .update(normalizeBaseURL(baseURL)) | ||
| .digest('hex') | ||
| .slice(0, 12)}` |
| const chunks: Buffer[] = [] | ||
| let received = 0 | ||
| for await (const chunk of response.message as AsyncIterable<Buffer>) { | ||
| received += chunk.length | ||
| if (received > SECURE_DOWNLOAD_MAX_BYTES) { |
| } | ||
| } | ||
|
|
||
| if (version.toLocaleLowerCase() === 'latest') { |
|
Security review on the new custom mirror path: I found two places where this should fail closed rather than warn/allow.
I validated both behaviors locally with targeted tests:
Focused validation result: |
|
Suggested change summary for a follow-up patch: Please do make sure that these changes make sense, if there are checks in place which will avoid scenario then feel free to avoid these changes.
|
There was a problem hiding this comment.
I think this PR should change before merge for two security reasons:
-
A custom
downloadBaseURLcurrently allows downloading and caching kubectl without a requiredchecksum.
This should fail closed, not warn. On self-hosted runners, an unverified custom-mirror binary can poison the tool cache and persist across later jobs. -
The SSRF guard only blocks literal private/link-local IPs.
A hostname that resolves to169.254.169.254, RFC1918 space, loopback, or similar internal ranges would still be fetched today. The custom download path should validate DNS-resolved addresses on each request/redirect hop and reject blocked targets.
Thanks.
Add downloadBaseURL, checksum, and downloadAuthToken inputs (closes #206)
Supports private kubectl mirrors (air-gapped / enterprise) with optional SHA256 verification and optional bearer-token auth for private artifact repositories. Default base URL is unchanged.
Security: https-only, blocks loopback/link-local/RFC1918 hosts, re-validates each redirect hop, strips the Authorization header on cross-origin redirects, masks the token via core.setSecret, and validates version format before any URL/path use.